#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QDebug"
#include "QFileDialog"
#include "fann.h"
#include <stdio.h>
#include "floatfann.h"
#include "ui_dialog.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    this->showMaximized();

    // create graphs
    this->ui->plotECG->addGraph();
    this->ui->plotECG->addGraph();
    this->ui->plotPCG->addGraph();
    this->ui->plotPCG->addGraph();
    // title
    this->ui->plotECG->plotLayout()->insertRow(0);
    this->ui->plotECG->plotLayout()->addElement(0, 0, new QCPPlotTitle(this->ui->plotECG, "ECG"));
    this->ui->plotPCG->plotLayout()->insertRow(0);
    this->ui->plotPCG->plotLayout()->addElement(0, 0, new QCPPlotTitle(this->ui->plotPCG, "PCG"));
    // axis labels
    this->ui->plotECG->xAxis->setLabel("t, s");
    this->ui->plotPCG->xAxis->setLabel("t, s");

    connect(this->ui->btnOpen, SIGNAL(clicked()), this, SLOT(slotOpen()));
    connect(this->ui->actionOpen, SIGNAL(triggered()), this, SLOT(slotOpen()));
    connect(this->ui->btnNeural, SIGNAL(clicked()), this, SLOT(slotNeural()));
    connect(this->ui->actionECGDerivative, SIGNAL(triggered()), this, SLOT(slotECGDerivative()));
    connect(this->ui->actionBreathing, SIGNAL(triggered()), this, SLOT(slotBreathing()));
    connect(this->ui->actionBreathingDerivative, SIGNAL(triggered()), this, SLOT(slotBreathingDerivative()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::average(int n)
{
    // Breathing type, inhalation (-1.0) or exhalation (1.0)
    QVector<double> *breathingType = breathing.type();

    QVector<int> beg;
    beg = *ecg.beginning();
    QVector<double> time;
    QVector<double> inhalation;
    QVector<double> exhalation;
    QVector<double>::iterator it;

    int min = pcg[n].getY()->size();
    for (int i = 1; i < beg.size(); i++)
    {
        if ((beg.at(i) - beg.at(i - 1)) < min)
        {
            min = beg.at(i) - beg.at(i - 1);
        }
    }

    // Initialization
    for (int i = 0; i < min; i++)
    {
        time << pcg[n].getX()->at(i);
        inhalation << 0.0;
        exhalation << 0.0;
    }

    // Number of PCG cycles at inhalation
    int inh = 0;
    // Number of PCG cycles at exhalation
    int exh = 0;

    for (int i = 0; i < beg.size() - 1; i++)
    {
        if (breathingType->at(beg.at(i)) == -1.0)
        {
            inh++;
            for (int j = 0; j < min; j++)
            {
                it = inhalation.begin() + j;
                *it += pcg[n].getY()->at(beg.at(i) + j);
            }
        }
        else if (breathingType->at(beg.at(i)) == 1.0)
        {
            exh++;
            for (int j = 0; j < min; j++)
            {
                it = exhalation.begin() + j;
                *it += pcg[n].getY()->at(beg.at(i) + j);
            }
        }
    }

    for (int i= 0; i < min; i++)
    {
        it = inhalation.begin() + i;
        *it /= inh;
        it = exhalation.begin() + i;
        *it /= exh;
    }

    this->ui->plotPCG->graph(0)->clearData();
    this->ui->plotPCG->graph(1)->clearData();
    // plot averaged PCG signal at inhalation
    this->ui->plotPCG->graph(0)->addData(time, inhalation);
    // plot averaged PCG signal at exhalation
    this->ui->plotPCG->graph(1)->addData(time, exhalation);
    // set the color of the PCG signal at exhalation to red
    this->ui->plotPCG->graph(1)->setPen(QPen(Qt::red));
    this->ui->plotPCG->graph(1)->setSelectedPen(QPen(Qt::red));
    this->ui->plotPCG->graph(1)->setSelectable(true);
    this->ui->plotPCG->graph(0)->setName(QString("At inhalation, %1 microphone").arg(n + 1));
    this->ui->plotPCG->graph(1)->setName(QString("At exhalation, %1 microphone").arg(n + 1));
    this->ui->plotPCG->legend->setVisible(true);
    // Allow user to drag axis ranges with mouse, zoom with mouse wheel and select graphs by clicking:
    this->ui->plotPCG->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
    this->ui->plotPCG->axisRect()->setRangeDrag(Qt::Horizontal);
    this->ui->plotPCG->axisRect()->setRangeZoom(Qt::Horizontal);
    // let the ranges scale themselves so graph 0 fits perfectly in the visible area:
    this->ui->plotPCG->rescaleAxes();
    this->ui->plotPCG->replot();
    this->ui->plotPCG->savePng(QString("%1.png").arg(n));
}

void MainWindow::slotOpen()
{
    QString fileName = QFileDialog::getOpenFileName(0, "Open Dialog", "", "*.dat");
    QFile file(fileName);

    this->ui->plotECG->graph(0)->clearData();

    if (file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QTextStream stream(&file);
        double t, s[6];

        while (!stream.atEnd())
        {
            stream >> t;

            if (t == 0.0)
                break;

            for (int i = 0; i < 6; i++)
            {
                stream >> s[i];
            }
            ecg.addPoint(t, s[4]);
            for (int i = 0; i < 4; i++)
            {
                pcg[i].addPoint(t, s[i]);
            }
            breathing.addPoint(t, s[5]);
        }
        // assign data to graph
        this->ui->plotECG->graph(0)->addData(*ecg.getX(), *ecg.getY());
        // let the ranges scale themselves so graph 0 fits perfectly in the visible area:
        this->ui->plotECG->rescaleAxes();
        // Allow user to drag axis ranges with mouse, zoom with mouse wheel and select graphs by clicking:
        this->ui->plotECG->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
        this->ui->plotECG->axisRect()->setRangeDrag(Qt::Horizontal);
        this->ui->plotECG->axisRect()->setRangeZoom(Qt::Horizontal);
        this->ui->plotECG->replot();
    }

    file.close();

    // ECG derivative
    ecg.derivative();

    this->average(3);

    // save image
    this->ui->plotECG->savePng("ECG.png");
}

void MainWindow::slotNeural()
{
    pcg[0].setBeginnings(*ecg.beginning());
    QVector<double> *s1 = pcg[0].getS1();
    QVector<double> *s2 = pcg[0].getS2();

    QFile training;
    training.setFileName("training.data");
    training.open(QIODevice::WriteOnly);
    QTextStream stream(&training);

    QFile distribution;
    distribution.setFileName("distribution.dat");
    distribution.open(QIODevice::WriteOnly);
    QTextStream stream2(&distribution);

    // number of training sets in the file
    stream << QString("%1 ").arg(pcg[0].numberOfPeriods() - 1);
    // number of inputs
    stream << QString("1 ");
    // number of outputs
    stream << QString("1\n");

    for (int i = 0; i < pcg[0].numberOfPeriods() - 1; i++)
    {
        stream << s1->at(i) << "\n";
        stream << "-1\n";
        stream << s2->at(i) << "\n";
        stream << "1\n";

        stream2 << s1->at(i) << "\t" << s2->at(i) << "\n";
    }

    training.close();

    const unsigned int num_input = 1;
    const unsigned int num_output = 1;
    const unsigned int num_layers = 3;
    const unsigned int num_neurons_hidden = 3;
    const float desired_error = (const float) 0.001;
    const unsigned int max_epochs = 500000;
    const unsigned int epochs_between_reports = 1000;

    struct fann *ann = fann_create_standard(num_layers, num_input, num_neurons_hidden, num_output);

    fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC);
    fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC);

    fann_train_on_file(ann, "training.data", max_epochs, epochs_between_reports, desired_error);

    fann_save(ann, "xor_float.net");

    fann_type *calc_out;
    //fann_type input[1];
    fann_type input[1];

    pcg[1].setBeginnings(*ecg.beginning());
    s1 = pcg[0].getS1();
    s2 = pcg[0].getS2();
    int s1_number = 0;
    int s2_number = 0;
    for (int i = 0; i < s1->size(); i++)
    {
        input[0] = s1->at(i);
        calc_out = fann_run(ann, input);
        if (calc_out[0] > -1.1 && calc_out[0] < -0.9)
            s1_number++;
        input[0] = s2->at(i);
        calc_out = fann_run(ann, input);
        if (calc_out[0] > 0.9 && calc_out[0] < 1.1)
            s2_number++;
    }

    qDebug() << s1_number << " / " << s1->size();
    qDebug() << s2_number << " / " << s2->size();

    fann_destroy(ann);

    QDialog *dialog = new QDialog;
    Ui::Dialog ui;
    ui.setupUi(dialog);
    dialog->show();
    QCPBars *histogram = new QCPBars(ui.customPlot->xAxis, ui.customPlot->yAxis);
    ui.customPlot->addPlottable(histogram);
    QVector<double> ticks;
    ticks << 1 << 2 << 3 << 4 << 5 << 6 << 7;
    ui.customPlot->xAxis->setTickVector(ticks);
    QVector<double> data;
    data << 0.2 << 0.3 << 0.5 << 0.3 << 0.4 << 0.6 << 0.3;
    histogram->setName("Test");
    histogram->setData(ticks, data);
    // let the ranges scale themselves so graph 0 fits perfectly in the visible area:
    ui.customPlot->rescaleAxes();
    // Allow user to drag axis ranges with mouse, zoom with mouse wheel and select graphs by clicking:
    ui.customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
    ui.customPlot->axisRect()->setRangeDrag(Qt::Horizontal);
    ui.customPlot->axisRect()->setRangeZoom(Qt::Horizontal);
    ui.customPlot->replot();
}

void MainWindow::slotSplit()
{
    /*QVector<double> *type = breathing.type();
    for (int i = 0; i < type->size(); i++)
    {
        qDebug() << type[i] << "\n";
    }*/
}

void MainWindow::slotECGDerivative()
{
    this->ui->plotECG->graph(0)->clearData();
    // assign data to graph
    this->ui->plotECG->graph(1)->addData(*ecg.getX(), *ecg.getDerivative());
    // let the ranges scale themselves
    this->ui->plotECG->rescaleAxes();
    this->ui->plotECG->replot();

    // save image
    this->ui->plotECG->savePng("ECGderivative.png");
}

void MainWindow::slotBreathing()
{
    this->ui->plotECG->graph(0)->clearData();
    // assign data to graph
    this->ui->plotECG->graph(0)->addData(*breathing.getX(), *breathing.getY());
    // let the ranges scale themselves
    this->ui->plotECG->rescaleAxes();
    this->ui->plotECG->replot();

    // save image
    this->ui->plotECG->savePng("Breathing.png");
}

void MainWindow::slotBreathingDerivative()
{
    this->ui->plotECG->graph(0)->clearData();
    // assign data to graph
    this->ui->plotECG->graph(0)->addData(*breathing.getX(), *breathing.getDerivative());
    // let the ranges scale themselves
    this->ui->plotECG->rescaleAxes();
    this->ui->plotECG->replot();

    // save image
    this->ui->plotECG->savePng("BreathingDerivative.png");
}
